/**
 * 
 */
package kr.co.insoft.framework.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;

/**
 * 권한 타입
 * 
 * <pre>
 * ALL : 모든 권한
 * CREATE : 생성 / 쓰기 권한
 * VIEW : 읽기 권한
 * UPDATE : 수정 권한
 * REMOVE : 삭제 권한
 * SCALEIN : Scale in 권한
 * SCALEOUT : Scale out 권한
 * SCRIPT : script 실행 권한
 * EVENT : event 실행 권한
 * MONITORING : 모니터링 권한
 * DOWNLOAD : 파일로 다운로드 권한
 * 
 * 예제) 
 * 	CREATE		:		01000000000		=		512
 * 	VIEW		:		00100000000		=		256
 * 	UPDATE		:		00010000000		=		128
 * 	REMOVE		:		00001000000		=		64
 * 	SCALEIN		:		00000100000		=		32
 * 	SCALEOUT		:		00000010000		=		16
 * 	EVENT		:		00000001000		=		8
 * 	SCRIPT		:		00000000100		=		4
 * 	MONITORING		:		00000000010		=		2
 * 	DOWNLOAD		:		00000000001		=		1
 * 	CREATE / REMOVE / MONITORING / VIEW / 		:		01101000010		=		834
 * </pre>
 * 
 * @author IN-SOFT Inc. GoodwillDD(sylee@in-soft.co.kr)
 * 
 */
public enum Authorities {
	CREATE(512L), VIEW(256L), UPDATE(128L), REMOVE(64L), SCALEIN(32L), SCALEOUT(
			16L), EVENT(8L), SCRIPT(4L), MONITORING(2L), DOWNLOAD(1L);

	static final int AUTH_LENGTH = Authorities.values().length;
	public static final String PREFIX = "F";
	public static final String authSeparator = "_";
	final Long code;

	private Authorities(Long code) {
		this.code = code;
	}

	public Long getCode() {
		return this.code;
	}

	private static String getAuthBinary(Long authCode) {
		return StringUtils.leftPad(Long.toBinaryString(authCode), AUTH_LENGTH,
				'0');
	}

	/**
	 * 모든 권한 반환
	 * 
	 * @return
	 */
	public static Long getFullAccessAuth() {
		return getAuthCode(values());
	}

	public static Long getViewAccessAuth() {
		return getAuthCode(Authorities.VIEW);
	}

	/**
	 * 권한 코드(10진법)을 권한으로 decode하여 반환한다.
	 * 
	 * @param auth
	 * @return
	 */
	public static Collection<Authorities> getDecodeAuth(Long auth) {
		Collection<Authorities> result = new ArrayList<Authorities>();
		String tmp = getAuthBinary(auth);
		String[] authorities = tmp.split("|");
		for (Authorities parsingAuth : values()) {
			if (authorities[parsingAuth.ordinal()].equals("1"))
				result.add(parsingAuth);
		}
		return result;
	}

	public static Collection<Authorities> getDecodeAuth(String auth) {
		Collection<Authorities> result = new ArrayList<Authorities>();
		String tmp = getAuthBinary(Long.parseLong(auth));
		String[] authorities = tmp.split("|");
		for (Authorities parsingAuth : values()) {
			if (authorities[parsingAuth.ordinal()].equals("1"))
				result.add(parsingAuth);
		}
		return result;
	}

	/**
	 * 권한을 형식에 맞춰 생성한다.
	 * 
	 * <pre>
	 * 	Fxxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx,888
	 * </pre>
	 * 
	 * @param targetCode
	 * @param authcode
	 * @return
	 */
	public static String makeAuth(String targetCode, Long authcode) {
		StringBuffer auth = new StringBuffer();
		auth.append(PREFIX);
		auth.append(targetCode);
		auth.append(authSeparator);
		auth.append(authcode);
		return auth.toString();
	}

	/**
	 * 권한을 형식에 맞춰 생성한다.
	 * 
	 * <pre>
	 * 	Fxxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx,888
	 * </pre>
	 * 
	 * @param targetCode
	 * @param authcode
	 * @return
	 */
	public static String makeAuth(String targetCode, String authcode) {
		StringBuffer auth = new StringBuffer();
		auth.append(PREFIX);
		auth.append(targetCode);
		auth.append(authSeparator);
		auth.append(authcode);
		return auth.toString();
	}

	/**
	 * 권한을 체크하여 해당하는 권한이 있는 code만을 반환한다.
	 * 
	 * @param auths
	 * @param checkAuth
	 * @return
	 */
	public static HashSet<String> parseAuth(
			Collection<? extends GrantedAuthority> auths, Authorities checkAuth) {
		try {
			Assert.notNull(auths);
		} catch (IllegalArgumentException e) {
			return new HashSet<String>();
		}

		String[] authParse = null;
		String targetCode = null;
		String authcode = null;
		HashSet<String> enabledTargetCode = new HashSet<String>();
		for (GrantedAuthority auth : auths) {
			if (auth.getAuthority().startsWith(PREFIX)) {
				authParse = auth.getAuthority().split(authSeparator);
				targetCode = authParse[0].replace(PREFIX, "");
				authcode = authParse[1];
				if (isEnabledAuth(authcode, checkAuth)) {
					enabledTargetCode.add(targetCode);
				}
			}
		}
		return enabledTargetCode;
	}

	/**
	 * 해당 권한이 있는지 확인한다.
	 * 
	 * @param authcode
	 * @param auth
	 * @return
	 */
	public static boolean isEnabledAuth(Long authcode, Authorities auth) {
		return getDecodeAuth(authcode).contains(auth);
	}

	/**
	 * 해당 권한이 있는지 확인한다.
	 * 
	 * @param authcode
	 * @param auth
	 * @return
	 */
	public static boolean isEnabledAuth(String authcode, Authorities auth) {
		return getDecodeAuth(Long.parseLong(authcode)).contains(auth);
	}

	/**
	 * View 권한이 있는지 확인한다.
	 * 
	 * @param authcode
	 * @return
	 */
	public static boolean isEnabledViewAuth(Long authcode) {
		return isEnabledAuth(authcode, Authorities.VIEW);
	}

	/**
	 * View 권한이 있는지 확인한다.
	 * 
	 * @param authcode
	 * @return
	 */
	public static boolean isEnabledViewAuth(String authcode) {
		return isEnabledAuth(authcode, Authorities.VIEW);
	}

	/**
	 * 중복을 제거하고 권한 코드를 생성한다.
	 * 
	 * @param auths
	 * @return
	 */
	public static Long getAuthCode(Authorities... auths) {
		HashSet<Authorities> auth = new HashSet<Authorities>(
				Arrays.asList(auths));
		Long sum = 0L;
		for (Authorities tmp : auth)
			sum += tmp.code;
		return sum;
	}
}
